package utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.logging.Logger;
import java.util.stream.Stream;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.*;

import org.docx4j.Docx4J;
import org.docx4j.Docx4jProperties;
import org.docx4j.XmlUtils;
import org.docx4j.convert.in.xhtml.XHTMLImporterImpl;
import org.docx4j.convert.out.HTMLSettings;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.P;
import org.docx4j.wml.R;
import org.jsoup.Jsoup;
import org.w3c.tidy.Tidy;

import utils.EmlMessage.Content;

public class EmlParser {

    private boolean isDebug = false;

    public static void main(String[] args) throws FileNotFoundException, MessagingException, IOException, Exception {
        EmlParser parser = new EmlParser();
        File out = new File("d:/test/测试5.docx");
        parser.toDoc(new File("d:/test/紫霞仙子_.eml"), out, null);
        //String s = parser.toHtml(new FileInputStream(new File("d:/test/测试2+图片_.eml")));
//        System.out.println(out.getParent());

    }

    private Message read(InputStream is) throws Exception {
        return new MimeMessage(Session.getInstance(System.getProperties()), is);
    }

    public void toDoc(File input, File output,ContentHandler handler) throws Exception {
        try (FileInputStream is = new FileInputStream(input); OutputStream os = new FileOutputStream(output)) {
            toDoc(is, os,"d:/test/",handler);
        }
    }

    public void toDoc(InputStream is, OutputStream os,String attachFilePath, ContentHandler handler) throws Exception {
        List<Content> parts = null;
        if (handler == null) {
            Logger.getAnonymousLogger().info("ContentHandler was null ,using default ContentHandler");
            handler = new DefaultContentHandler();
        }
        Message message = read(is);
        EmlMessage emlMessage = new EmlMessage(message);
        if(emlMessage.getMessage().isMimeType("multipart/mixed")){
            //带有附件的文本
            parts = emlMessage.getContent(attachFilePath);
        }else if(emlMessage.getMessage().isMimeType("multipart/related")){
            //带有图片的文本
            parts = emlMessage.getContent(attachFilePath);
        }else {
            parts = emlMessage.getContent();
        }
        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();

        MainDocumentPart mainPart = wordMLPackage.getMainDocumentPart();
        addHeader(mainPart, message);
        boolean hasText = false;
        for (Content part : parts) {
            hasText = handler.hasText(wordMLPackage, message, part);
            if(hasText){
                break;
            }
        }
        for (Content part : parts) {
            handler.addContent(wordMLPackage, message, part,hasText);
        }
        if (isDebug) {
            logXml(wordMLPackage);
        }
        Docx4J.save(wordMLPackage, os, Docx4J.FLAG_SAVE_ZIP_FILE);

    }

    private void logXml(WordprocessingMLPackage wordMLPackage) {
        System.out.println(XmlUtils.marshaltoString(wordMLPackage.getMainDocumentPart().getJaxbElement(), true, true));

    }

    public void setDebugMode(boolean isDebug) {
        this.isDebug = isDebug;
    }

    public String toHtml(File file) throws Exception {
        try (InputStream is = new FileInputStream(file)) {
            return toHtml(is);
        }
    }

    public String toHtml(InputStream is) throws Docx4JException {
        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(is);

        HTMLSettings htmlSettings = Docx4J.createHTMLSettings();
        htmlSettings.setWmlPackage(wordMLPackage);

        Docx4jProperties.setProperty("docx4j.Convert.Out.HTML.OutputMethodXML", true);

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        Docx4J.toHTML(htmlSettings, os, Docx4J.FLAG_EXPORT_PREFER_XSL);
        return os.toString();
    }

    private void addHeader(MainDocumentPart mainPart, Message message) throws Exception {
        mainPart.addParagraphOfText("Subject:" + (message.getSubject()!=null ? message.getSubject():""));
        mainPart.addParagraphOfText("sendDate:" +  new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(message.getSentDate()));
        if(message.getHeader("Content-Transfer-Encoding")!=null&&message.getHeader("Content-Transfer-Encoding")[0].equals("8Bit")){
            mainPart.addParagraphOfText("From:"+ MimeUtility.decodeText(message.getHeader("From")[0].replace("\"","")));

            mainPart.addParagraphOfText("To:"+ MimeUtility.decodeText(message.getHeader("To")[0].replace("\"","")));
            if(message.getRecipients(RecipientType.CC) != null){
                mainPart.addParagraphOfText("Cc:" +MimeUtility.decodeText(message.getHeader("Cc")[0].replace("\"","")));
            }
            if(message.getRecipients(RecipientType.BCC)!=null){
                mainPart.addParagraphOfText("Bcc:"+ MimeUtility.decodeText(message.getHeader("Bcc")[0].replace("\"","")));
            }
        }else {
            mainPart.addParagraphOfText("From:" + message.getHeader("From")[0]);
            mainPart.addParagraphOfText("To:" + message.getHeader("To")[0]);
            if(message.getRecipients(RecipientType.CC) != null){
                mainPart.addParagraphOfText("Cc:" +message.getHeader("Cc")[0].replace("\"",""));
            }
            if(message.getRecipients(RecipientType.BCC)!=null){
                mainPart.addParagraphOfText("Bcc:"+message.getHeader("Bcc")[0]);
            }
        }
    }

    public Message createMessage(String to, String from, String subject, String body, List<File> attachments)
            throws AddressException, MessagingException {
        Message message = new MimeMessage(Session.getInstance(System.getProperties()));
        message.setFrom(new InternetAddress(from));
        message.setRecipients(RecipientType.TO, InternetAddress.parse(to));
        message.setSubject(subject);

        MimeBodyPart content = new MimeBodyPart();
        content.setText(body);

        Multipart multipart = new MimeMultipart();
        multipart.addBodyPart(content);

        if (attachments != null) {
            for (File file : attachments) {
                MimeBodyPart attachment = new MimeBodyPart();
                DataSource source = new FileDataSource(file);
                attachment.setDataHandler(new DataHandler(source));
                attachment.setFileName(file.getName());
                multipart.addBodyPart(attachment);
            }
        }
        message.setContent(multipart);
        return message;
    }

    public static String tidyHtml(String html) {
        Tidy tidy = new Tidy();
        tidy.setXHTML(true);
        StringWriter stringWriter = new StringWriter();
        tidy.parse(new StringReader(html), stringWriter);
        return stringWriter.getBuffer().toString();
    }

    public static interface ContentHandler {
        public boolean addContent(WordprocessingMLPackage wordMLPackage, Message message, Content part,boolean hasText) throws Exception;
        public boolean hasText(WordprocessingMLPackage wordMLPackage, Message message, Content part)throws Exception;
    }

    class DefaultContentHandler implements ContentHandler {
        @Override
        public boolean addContent(WordprocessingMLPackage wordMLPackage, Message message, Content part,boolean hasText) throws Exception {
            MainDocumentPart mainPart = wordMLPackage.getMainDocumentPart();
            if (part.isTextPlain()) {
                String content = part.getBody();
                String[] contents = content.split("\r\n");
                int size = contents.length;
                for(int i = 0; i< size; i++){
                    mainPart.addParagraphOfText(contents[i]);
                }
                return true;
            }
            if (part.isHtml() && !hasText) {
                String tidyHtml = tidyHtml(Jsoup.parse(part.getBody()).html());
                System.out.println(tidyHtml);
                XHTMLImporterImpl XHTMLImporter = new XHTMLImporterImpl(wordMLPackage);
                if(!tidyHtml.equals("")){
                    List<Object> xhtml = XHTMLImporter.convert(tidyHtml,null);
                    mainPart.getContent().addAll(xhtml);
                }else{
                    return false;
                }
            }
            if(part.isImage()){
                BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage,new File("d:/test/"+part.getPart().getFileName()));
                Inline inline = imagePart.createImageInline(null,null,0,1,false);
                ObjectFactory factory = new ObjectFactory();
                P paragraph = factory.createP();
                R run = factory.createR();
                org.docx4j.wml.Drawing drawing = factory.createDrawing();
                drawing.getAnchorOrInline().add(inline);
                run.getContent().add(drawing);
                paragraph.getContent().add(run);
                mainPart.addObject(paragraph);
            }
            return true;
        }

        @Override
        public boolean hasText(WordprocessingMLPackage wordMLPackage, Message message, Content part) throws Exception {
            if (part.isTextPlain()) {
                return true;
            }
            return false;
        }

    }

}
